package org.jeecg.modules.flowable.service.impl;

import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.EndEvent;
import org.flowable.engine.IdentityService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.modules.flowable.dto.FlowInstanceStartDTO;
import org.jeecg.modules.flowable.dto.FlowInstanceStopDTO;
import org.jeecg.modules.flowable.entity.FlowInstance;
import org.jeecg.modules.flowable.entity.FlowModel;
import org.jeecg.modules.flowable.entity.FlowOperationInfo;
import org.jeecg.modules.flowable.enums.FlowInstanceStatusEnum;
import org.jeecg.modules.flowable.enums.TaskOperationTypeEnum;
import org.jeecg.modules.flowable.mapper.FlowInstanceMapper;
import org.jeecg.modules.flowable.service.FlowInstanceParticipantService;
import org.jeecg.modules.flowable.service.FlowInstanceService;
import org.jeecg.modules.flowable.service.FlowModelService;
import org.jeecg.modules.flowable.service.FlowOperationInfoService;
import org.jeecg.modules.flowable.utils.BeanUtils;
import org.jeecg.modules.flowable.vo.FlowInstanceVO;
import org.jeecg.modules.flowable.vo.FlowModelVO;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class FlowInstanceServiceImpl extends ServiceImpl<FlowInstanceMapper, FlowInstance> implements FlowInstanceService {
    final TaskService taskService;
    final RuntimeService runtimeService;
    final IdentityService identityService;
    final FlowModelService flowModelService;
    final RepositoryService repositoryService;
    final FlowOperationInfoService flowOperationInfoService;
    final FlowInstanceParticipantService flowInstanceParticipantService;

    @Resource
    private FlowInstanceMapper flowInstanceMapper;

    @Override
    @Transactional
    public Result<Void> start(FlowInstanceStartDTO dto) {
        String userId = dto.getUserId();
        String businessKey = dto.getBusinessKey();

        if (ObjectUtils.isEmpty(businessKey)) {
            return Result.error("启动的业务流程不存在！");
        }
        FlowModel flowModel = flowModelService.getByBusinessKey(businessKey);
        if (Objects.isNull(flowModel)) {
            return Result.error("启动的业务流程不存在！");
        }

        // 实例校验
        Result<Void> verify = this.verifyBusinessDataId(dto.getBusinessKey(), dto.getBusinessDataId());
        if (!verify.isSuccess()) {
            // 已经存在，尝试重试
            return this.retry(dto);
        }
        String processDefinitionId = flowModel.getProcessDefinitionId();

        // 查询实例是否正确
        ProcessDefinition processDefinition = this.repositoryService
                .createProcessDefinitionQuery()
                .processDefinitionId(processDefinitionId)
                .latestVersion()
                .singleResult();
        if (Objects.isNull(processDefinition)) {
            return Result.error("该申请流程不存在,请联系系统管理员！");
        }
        if (processDefinition.isSuspended()) {
            return Result.error("该申请流程已经挂起,请联系系统管理员!");
        }
        // 流程启动初始化参数
        Map<String, Object> variableMap = new HashMap<>();
        if (!ObjectUtils.isEmpty(dto.getBusinessData())) {
            variableMap = JSONUtil.toBean(dto.getBusinessData(), new TypeReference<Map<String, Object>>() {
            }, true);
        }
        this.identityService.setAuthenticatedUserId(userId.toString());
        // 启动流程实例
        ProcessInstance processInstance = this.runtimeService.createProcessInstanceBuilder()
                .processDefinitionId(processDefinitionId)
                .name(processDefinition.getName())
                .variables(variableMap)
                .start();
        String processInstanceId = processInstance.getId();
        String processInstanceName = processInstance.getName();
        // 记录表单绑定记录
        FlowInstance instance = new FlowInstance();
        instance.setId(processInstanceId);
        //instance.setId(IdUtil.fastSimpleUUID());
        instance.setName(processInstanceName);
        instance.setBusinessKey(flowModel.getBusinessKey());
        instance.setBusinessName(flowModel.getBusinessName());
        instance.setBusinessData(dto.getBusinessData());
        instance.setBusinessDataId(dto.getBusinessDataId());
        instance.setProcessDefinitionId(processDefinition.getId());
        instance.setProcessDefinitionKey(processDefinition.getKey());
        instance.setOperationVersion(BigDecimal.ONE);
        instance.setCreateTime(LocalDateTime.now());
        instance.setCreateUserId(dto.getUserId());
        instance.setCreateUserName(dto.getUserName());
        instance.setInstanceStatus(FlowInstanceStatusEnum.ONGOING.getCode());
        this.save(instance);
        this.identityService.setAuthenticatedUserId(null);
        // 组装实例参与者列表
        Result<Void> loadParticipantResult = this.flowInstanceParticipantService.saveInstanceParticipant(instance);
        if (!loadParticipantResult.isSuccess()) {
            throw new JeecgBootException(loadParticipantResult.getMessage());
        }
        //this.initSubmit(instance, variableMap);
        return Result.OK();
    }

    @Override
    public Result<Void> stop(FlowInstanceStopDTO dto) {
        String processInstanceId = dto.getProcessInstanceId();
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        if (null == processInstance) {
            return Result.error("流程已结束,或流程不存在！");
        }
        List<EndEvent> endNodes = this.flowModelService.findEndFlowElement(processInstance.getProcessDefinitionId());
        if (CollectionUtils.isEmpty(endNodes)) {
            return Result.error("执行流程停止失败！");
        }
        String endId = endNodes.get(0).getId();
        // 执行终止
        List<Execution> executions = runtimeService.createExecutionQuery().parentId(processInstanceId).list();
        List<String> executionIds = executions.stream().map(Execution::getId).collect(Collectors.toList());
        this.runtimeService.createChangeActivityStateBuilder().moveExecutionsToSingleActivityId(executionIds, endId).changeState();

        // 添加操作记录，流程操作没有节点信息（拓展可以添加操作时节点所处位置）
        FlowOperationInfo operationInfo = new FlowOperationInfo();
        operationInfo.setId(IdUtil.fastSimpleUUID());
        operationInfo.setProcessInstanceId(dto.getProcessInstanceId());
        operationInfo.setOperationUserId(dto.getUserId());
        operationInfo.setOperationUserName(dto.getUserName());
        operationInfo.setOperationTime(LocalDateTime.now());
        operationInfo.setMessage(dto.getMessage());
        operationInfo.setOperatorType(TaskOperationTypeEnum.TERMINATION.getCode());
        this.flowOperationInfoService.save(operationInfo);

        return Result.OK();
    }

    @Override
    public void updateInstanceStatus(String processInstanceId, FlowInstanceStatusEnum status) {
        FlowInstance instance = this.getById(processInstanceId);
        if (Objects.isNull(instance)) {
            return;
        }
        instance.setInstanceStatus(status.getCode());
        instance.setOperationVersion(instance.getOperationVersion().add(BigDecimal.ONE));
        this.updateById(instance);
    }

    /**
     * 重新发起
     */
    public Result<Void> retry(FlowInstanceStartDTO dto) {
        String businessKey = dto.getBusinessKey();
        String businessDataId = dto.getBusinessDataId();
        FlowInstance instance = this.getInstanceByBusinessDataId(businessKey, businessDataId);
        if (Objects.isNull(instance)) {
            return Result.error("重试失败，请刷新后重试 ！！");
        }
        if (!FlowInstanceStatusEnum.WAIT.getCode().equals(instance.getInstanceStatus())) {
            return Result.error("重试失败，流程正常进行中，请勿重新发起 ！！");
        }

        // 流程启动初始化参数
        Map<String, Object> variableMap = new HashMap<>();
        if (!ObjectUtils.isEmpty(dto.getBusinessData())) {
            variableMap = JSONUtil.toBean(dto.getBusinessData(), new TypeReference<Map<String, Object>>() {
            }, true);
        }
        instance.setBusinessData(dto.getBusinessData());
        instance.setInstanceStatus(FlowInstanceStatusEnum.ONGOING.getCode());
        this.updateById(instance);
        // 组装实例参与者列表
        Result<Void> loadParticipantResult = this.flowInstanceParticipantService.saveInstanceParticipant(instance);
        if (!loadParticipantResult.isSuccess()) {
            throw new JeecgBootException(loadParticipantResult.getMessage());
        }
        this.initSubmit(instance, variableMap);
        return Result.OK();
    }

    /**
     * 放重复提交，相同key的id不能重复
     */

    private Result<Void> verifyBusinessDataId(String businessKey, String businessDataId) {
        FlowInstance instance = this.getInstanceByBusinessDataId(businessKey, businessDataId);
        if (Objects.isNull(instance)) {
            return Result.OK();
        }
        return Result.error("请勿重复发起实例！");
    }

    /**
     * 获取业务绑定的实例
     */
    @Override
    public FlowInstance getInstanceByBusinessDataId(String businessKey, String businessDataId) {
        // 业务关联参数为空，不进行校验
        if (ObjectUtils.isEmpty(businessDataId) || ObjectUtils.isEmpty(businessKey)) {
            return null;
        }
        businessKey = businessKey.toLowerCase();
        LambdaQueryWrapper<FlowInstance> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(FlowInstance::getBusinessKey, businessKey);
        queryWrapper.eq(FlowInstance::getBusinessDataId, businessDataId);
        List<FlowInstance> instanceList = this.baseMapper.selectList(queryWrapper);
        if (CollectionUtils.isEmpty(instanceList)) {
            return null;
        }
        return instanceList.get(0);
    }

    @Override
    public Result<FlowInstanceVO> info(String instanceId) {
        FlowInstance flowInstance = this.baseMapper.selectById(instanceId);
        FlowInstanceVO vo = BeanUtils.convertTo(flowInstance, FlowInstanceVO::new);
        // 获取查询任务
        Task task = taskService.createTaskQuery().processInstanceId(flowInstance.getId()).singleResult();
        if (Objects.nonNull(task)) {
            vo.setCurTaskId(task.getId());
            vo.setCurTaskName(task.getName());
        }
        if (FlowInstanceStatusEnum.FINISH.getCode().equals(flowInstance.getInstanceStatus())) {
            vo.setCurTaskName("完成");
        }
        if (FlowInstanceStatusEnum.TERMINATION.getCode().equals(flowInstance.getInstanceStatus())) {
            vo.setCurTaskName("取消");
        }
        String processDefinitionId = flowInstance.getProcessDefinitionId();
        String[] tmp = processDefinitionId.split(":");
        System.out.println(tmp[2] +"\n");
        Result<FlowModelVO> modelResult = this.flowModelService.loadBpmnXmlByModelId(tmp[2]);
        if (modelResult.isSuccess()) {
            vo.setModelXml(modelResult.getResult().getXml());
        }
        return Result.OK(vo);
    }

    @Override
    public FlowInstance searchByBusinessKeyAndBusinessDataId(String businessKey, String businessDataId) {
        LambdaQueryWrapper<FlowInstance> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(FlowInstance::getBusinessKey, businessKey);
        queryWrapper.eq(FlowInstance::getBusinessDataId, businessDataId);
        return flowInstanceMapper.selectOne(queryWrapper);
    }

    public Result<Void> initSubmit(FlowInstance instance, Map<String, Object> variableMap) {
        String instanceId = instance.getId();
        List<Task> taskList = this.taskService.createTaskQuery().processInstanceId(instanceId).list();
        if (CollectionUtils.isEmpty(taskList)) {
            return Result.error("当前实例没有进行中的任务！");
        }
        Task task = taskList.get(0);
        // 记录流转状态
        FlowOperationInfo operationInfo = new FlowOperationInfo();
        operationInfo.setId(IdUtil.fastSimpleUUID());
        operationInfo.setTaskId(task.getId());
        operationInfo.setTaskName(task.getName());
        operationInfo.setActivityId(task.getTaskDefinitionKey());
        operationInfo.setOperatorType(TaskOperationTypeEnum.ACTIVATE.getCode());
        operationInfo.setProcessInstanceId(instanceId);
        operationInfo.setOperationUserId(instance.getCreateUserId());
        operationInfo.setOperationUserName(instance.getCreateUserName());
        operationInfo.setOperationTime(LocalDateTime.now());
        operationInfo.setMessage(TaskOperationTypeEnum.ACTIVATE.getDesc());
        this.flowOperationInfoService.save(operationInfo);
        // 执行流转任务
        this.taskService.complete(task.getId(), variableMap);
        return Result.OK();
    }
}
